from Box2D import b2_dynamicBody, b2Vec2
from libavg import avg
import components
from random import randint
import Constants
import math

class Brick(avg.DivNode):
    
    def __init__(self, parent, position, size, life, angle = 0.0, **kwargs):
        super(Brick, self).__init__(**kwargs)

        self.__life = life
        self.__actorId = -1

        if parent:
            parent.appendChild(self)
            self.__level = parent

        self.pos = position
        self.size = size
        
        #Physics
        self.__body = self.__level.world.Rectangle(position=self.pos, size=self.size)
        self.__body.userData = self
        self.__body.angle = angle
        self.__body.fixtures[0].filterData.maskBits = 176
        self.__body.fixtures[0].filterData.categoryBits = 2
        self.angle = self.__body.angle
        
        self.__tex = avg.ImageNode(parent=self.__level, size=self.size, pos=self.pos, angle=self.angle, gamma = (1.0, 1.0, 1.0))
        self.__tex.setBitmap(avg.SVG('resources/textures/brick/bgr.svg', False).renderElement('Layer1'))
        self.__updateImage()
                
        self.__onFrameHandler = avg.Player.get().setOnFrameHandler(self.__update)

    def __update(self):
        self.pos = (self.__body.position.x * Constants.METERS2PIXELS - self.size[0] / 2, self.__body.position.y * Constants.METERS2PIXELS  - self.size[1] / 2)
        self.angle = self.__body.angle
        
        if(self.__life == 0):
            # Release Item
            itemRelease = randint(1, 100) <= self.__level.getItemProbabilityForLevel()
            if itemRelease:
                self.__level.items.append(Item(parent=self.__level, position=self.__body.position * Constants.METERS2PIXELS, actorId=self.__actorId))
            self.__level.DestroyBrick(self)
            
    def __updateImage(self):
        if self.__life == 0:
            return
        self.__tex.intensity = Constants.BRICK_COLORS[(self.__life-1) if (self.__life-1) <= Constants.BRICK_COLORS.__len__() else Constants.BRICK_COLORS.__len__() - 1]
   
    def onBeginContact(self, o):
        pass
            
    def onEndContact(self, o):
        if(isinstance(o, components.Actor.Ball)):
            if(self.__life > 0):
                self.__actorId = o.getActor()
                self.__level.actors[self.__actorId].increasePoints(Constants.BRICK_BALL_HIT)
            else:
                self.__level.actors[self.__actorId].increasePoints(Constants.BRICK_BALL_DESTORY)
            self.__life = (self.__life - 1) if self.__life > 0 else 0
            self.__updateImage()
    
    def destroy(self):
        if self.__onFrameHandler:
            avg.Player.get().clearInterval(self.__onFrameHandler)
        self.__onFrameHandler = None
        self.__level.world.DestroyBody(self.__body)
        self.body = None   
        self.__tex.unlink(True)
        self.unlink(True)

class Item(avg.ImageNode):
    
    def __init__(self, parent, position, actorId, itemId = -1, **kwargs):
        super(Item, self).__init__(**kwargs)
        
        self.__radius = Constants.ITEM_SIZE_RADIUS
        self.__actorId = actorId
        self.__collected = False

        if parent:
            parent.appendChild(self)
            self.__level = parent

        self.pos = (position[0] - self.__radius, position[1] - self.__radius)             
        self.size = (self.__radius * 2, self.__radius * 2)
        self.angle = -(math.pi/2) if self.__actorId == 1 else (math.pi/2)
        
        if itemId != -1:
            self.__itemId = itemId
        else:
            self.__itemId = self.__level.getRandomItemID()
        
        self.href="resources/textures/item/sItem_"+ (self.__itemId).__str__() + ".png"
        
        # create physical body
        self.__body = self.__level.world.Circle(position=position, radius=self.__radius)
        self.__body.userData = self
        self.__body.type = b2_dynamicBody
        self.bullet = True
        self.__body.fixtures[0].filterData.maskBits = 13
        self.__body.fixtures[0].filterData.categoryBits = 16
        self.__body.linearVelocity = b2Vec2(-5, 0) if self.__actorId == 0 else b2Vec2(5, 0)
        
        self.__onFrameHandler = avg.Player.get().setOnFrameHandler(self.__update)
    
    def __update(self):
        self.pos = (self.__body.position.x * Constants.METERS2PIXELS - self.__radius, self.__body.position.y * Constants.METERS2PIXELS - self.__radius)
        if (not self.__body.awake or self.__collected):
            self.__level.DestroyItem(self)    

    def onBeginContact(self, o):
        if isinstance(o, components.Actor.Shuttle):
            actor = self.__level.actors[o.getActor()]
            if actor.shuttles[0].inRepairMode() == False:
                self.__collected = True
                actor.setBonus(self.__itemId)
            
        if isinstance(o, components.Level.Wall):
            if o.getActor() is not None and o.getActor() == self.__actorId:
                self.__collected = True
            
    def onEndContact(self, o):
        pass
       
    def destroy(self):
        if self.__onFrameHandler:
            avg.Player.get().clearInterval(self.__onFrameHandler)
        self.__onFrameHandler = None
        
        self.__level.world.DestroyBody(self.__body)
        self.__body = None
        self.unlink(True)
        
class Meteor(avg.DivNode):
    
    def __init__(self, parent, actorId, **kwargs):
        super(Meteor, self).__init__(**kwargs)
        
        self.__radius = randint(Constants.METEOR_RADIUS / 2, Constants.METEOR_RADIUS)
        self.__actorId = actorId

        if parent:
            parent.appendChild(self)
            self.__level = parent

        self.pos = (-randint(0, 500) if actorId == 0 else self.__level.size.x + randint(0, 500), randint(0, self.__level.size.y) - self.__radius)      
        self.size = (self.__radius * 2, self.__radius * 2)
        self.angle = 0 if self.__actorId == 1 else math.pi
        
        self.__image = avg.ImageNode(parent=self, href="resources/textures/level/asteroid.png", size=self.size)
        
        # create physical body
        self.__body = self.__level.world.Circle(position=(self.pos.x + self.__radius, self.pos.y + self.__radius), radius=self.__radius)
        self.__body.userData = self
        self.__body.type = b2_dynamicBody
        self.bullet = True
        self.__body.fixtures[0].filterData.maskBits = 8 if self.__actorId == 0 else 4
        self.__body.fixtures[0].filterData.categoryBits = 64
        self.__body.linearVelocity = b2Vec2(2, 0) if self.__actorId == 0 else b2Vec2(-2, 0)
        
        self.__onFrameHandler = avg.Player.get().setOnFrameHandler(self.__update)
    
    def __update(self):
        if not self.__body:
            return
        
        if self.__body.awake:
            self.pos = (self.__body.position.x * Constants.METERS2PIXELS - self.__radius, self.__body.position.y * Constants.METERS2PIXELS - self.__radius)
            self.__body.linearVelocity *= 1.01
            if abs(self.__body.linearVelocity.x) > Constants.METEOR_MAX_SPEED: 
                self.__body.linearVelocity = b2Vec2(5, 0) if self.__actorId == 0 else b2Vec2(-5, 0)
        else:
            self.__level.world.DestroyBody(self.__body)
            self.__body = None           
               
    def onBeginContact(self, o):
        self.active = False
        self.__image.active = False
        self.__body.awake = False
    
    def onEndContact(self, o):
        pass
    
    def destroy(self):
        if self.__onFrameHandler:
            avg.Player.get().clearInterval(self.__onFrameHandler)
        self.__onFrameHandler = None
        
        if self.__body:
            self.__level.world.DestroyBody(self.__body)
        self.__body = None
        if self.__image:
            self.__image.active = False
            self.__image.unlink(True)
        self.__image = None
        self.unlink(True)      
        
class Event(avg.DivNode):
    def __init__(self, parent, node, eventHandler=None, intervalHandler=None, maxDuration = 30000, maxInterval = 300000, loop=True, duration=None, **kwargs):
        super(Event, self).__init__(**kwargs)
        
        if parent:
            parent.appendChild(self)
            self.__level = parent
            
        self.__node = node
        self.__node.active = False
        
        self.__loop = loop
        
        self.__eventHandler = eventHandler
        self.__intervalHandler = intervalHandler
        self.__onStartHandler = None
        self.__onCreateHandler = None
            
        self.__maxInterval = maxInterval
        self.__maxDuration = maxDuration
        
        if duration is not None:
            self.__duration = duration
        else:
            self.__duration = None
        
        self.__randInterval()
    
    def getNode(self):
        return self.__node
    
    def __randInterval(self):
        self.__interval = randint(self.__maxInterval/2, self.__maxInterval)
        self.__onStartHandler = avg.Player.get().setInterval(self.__interval, self.__start)
    
    def __start(self):
        avg.Player.get().clearInterval(self.__onStartHandler)
        
        if self.__duration is None:
            self.__onCreateHandler = avg.Player.get().setInterval(randint(self.__maxDuration/2, self.__maxDuration), self.__restart)
        else:
            self.__onCreateHandler = avg.Player.get().setInterval(self.__duration, self.__restart)
            
        if self.__eventHandler:
            self.__eventHandler(self)
        self.__node.active = True

    def __restart(self):
        avg.Player.get().clearInterval(self.__onCreateHandler) 

        if self.__intervalHandler:
            self.__intervalHandler(self)
        self.__node.active = False
        if self.__loop:
            self.__randInterval()
        else:
            self.__level.DestroyEvent(self)    
    
    def destroy(self):
        if self.__onStartHandler:
            avg.Player.get().clearInterval(self.__onStartHandler)
        self.__onStartHandler = None     
               
        if self.__onCreateHandler:
            avg.Player.get().clearInterval(self.__onCreateHandler)
        
        self.__onCreateHandler = None
        self.__eventHandler = None
        self.__intervalHandler = None
        self.__node.destroy()
        self.active = False
        self.unlink(True)

         
class Gravity(avg.ImageNode):
    
    def __init__(self, parent, position = (0, 0), radius = 120, force = 9.81, **kwargs):
        super(Gravity, self).__init__(**kwargs)
        
        #Init parent
        if parent:
            parent.appendChild(self)
            self.__level = parent
        
        if radius is None:
            self.__radius = Constants.BALL_DEFAULT_RADIUS
        else:
            self.__radius = radius

        self.__force = force * self.__radius / 25
   
        self.pos = (position[0] - self.__radius, position[1] - self.__radius)           
        self.size = (self.__radius * 2, self.__radius * 2)
        self.href = "resources/textures/gravity/blackhole.png"
        self.angle = 0

        self.__onFrameHandler = avg.Player.get().setOnFrameHandler(self.__update)        
    
    def __update(self):
        if not self.active:
            return
        
        self.angle -= 0.01
        
        for ball in self.__level.balls:
            if ball.isFrozen() == False and ball.isInRepairMode() == False:
                if self.__dist(ball) < self.__radius * Constants.PIXELS2METERS:
                    gravity = self.__gravity(ball)
                    if isinstance(ball.getBody().position, b2Vec2):
                        ball.getBody().ApplyForce(gravity, ball.getBody().position)
            
    def __dist(self, o):
        return (b2Vec2(self.pos.x + self.__radius, self.pos.y + self.__radius) * Constants.PIXELS2METERS - o.getBody().position).length
    
    def __gravity(self, o):
        return (b2Vec2(self.pos.x + self.__radius, self.pos.y + self.__radius) * Constants.PIXELS2METERS -  o.getBody().position) / o.getBody().linearVelocity.length * min(self.__radius / self.__dist(o) / 2, 1) * self.__force
            
    def destroy(self):
        if self.__onFrameHandler:
            avg.Player.get().clearInterval(self.__onFrameHandler)
        self.__onFrameHandler = None
        
        self.active = False
        self.unlink(True)

    
class Bumper(avg.ImageNode):
    
    def __init__(self, parent, position=(-500, -500), radius=50,  **kwargs):
        super(Bumper, self).__init__(**kwargs)

        if parent:
            parent.appendChild(self)
            self.__level = parent

        self.__radius = radius
        self.gamma = (0.2, 0.2, 0.2)
        self.intensity = (1.0, 0.8, 0.0)
        
        self.size=(self.__radius * 2, self.__radius * 2)
        self.pos = (position[0] - self.__radius, position[1] - self.__radius)
        self.href = "resources/textures/item/bumper.png"
                
        # add physical body
        self.__body = self.__level.world.Circle(position=position, radius=self.__radius)
        self.__body.userData = self
        self.__body.fixtures[0].filterData.maskBits = 176
        self.__body.fixtures[0].filterData.categoryBits = 2
        self.bullet = True     
        
    def onBeginContact(self, o):
        pass
    
    def onEndContact(self, o):
        pass
    
    def getRadius(self):
        return self.__radius
    
    def setBodyPosition(self, pos):
        self.__body.position = pos
    
    def destroy(self):
        self.__level.world.DestroyBody(self.__body)
        self.active = False
        self.unlink(True)                
        
        
        
        
        
        
        
        
        
        
        